local Aloft = Aloft
if not Aloft then return end
local AloftModules = AloftModules
if not AloftModules then return end

-----------------------------------------------------------------------------

-- AloftModules:AddInitializer("AloftHealthBarDeficit", function()

if not Aloft:GetModule("HealthBar", true) then return end

-----------------------------------------------------------------------------

-- TODO: fix AloftFrame:UpdateAll() to take reference to this module out once it merges with AloftHealthBar
local AloftHealthBarDeficit = Aloft:NewModule("HealthBarDeficit", Aloft, "AceEvent-3.0", "AceTimer-3.0")
-- AloftHealthBarDeficit.dynamic = "AloftHealthBarDeficit"

local SML = LibStub("LibSharedMedia-3.0")

-----------------------------------------------------------------------------

AloftHealthBarDeficit.namespace = "healthBarDeficit"
AloftHealthBarDeficit.defaults =
{
	profile =
	{
		enable = false,
	},
}

-----------------------------------------------------------------------------

-- backdrop table for initializing visible nameplates
local backdropTable =
{
	-- tile = true,
	-- tileSize = 16,
	bgFile = nil,
	edgeSize = 16,
	edgeFile = nil,
	insets = { left = 0, right = 0, top = 0, bottom = 0 },
}

-- backdrop table for resetting nameplates when they are hidden
local defaultBackdropTable =
{
	-- tile = true,
	-- tileSize = 0,
	bgFile = nil,
	-- edgeSize = 0,
	edgeFile = nil,
	insets = { left = 0, right = 0, top = 0, bottom = 0 },
}

-----------------------------------------------------------------------------

local CreateFrame = CreateFrame

-----------------------------------------------------------------------------

function AloftHealthBarDeficit:OnInitialize()
	if self.db ~= Aloft.AloftDB:GetNamespace(self.namespace, true) then self.db = Aloft.AloftDB:RegisterNamespace(self.namespace, self.defaults) end
	AloftHealthBarDeficit.healthBarDB = Aloft.AloftDB:GetNamespace("healthBar")
end

function AloftHealthBarDeficit:OnEnable()
	self:UpdateAll()
	self:UpdateSizesAll()
end

function AloftHealthBarDeficit:OnDisable()
	self:UnregisterAllEvents()
	self:UnregisterAllMessages()
	self:CancelAllTimers()

	for aloftData in Aloft:IterateNameplates() do
		self:ReleaseDeficitFrame(aloftData)
		aloftData.healthBar:Show()
	end
end

-----------------------------------------------------------------------------

local dataRequiredList = { }
function AloftHealthBarDeficit:RequiresData()
	self:RegisterEvents()
end

function AloftHealthBarDeficit:RegisterEvents()
	self:UnregisterAllEvents()
	self:UnregisterAllMessages()
	self:CancelAllTimers()

	if self.db.profile then
		self:RegisterMessage("Aloft:SetupFrame", "SetupFrame")
		self:RegisterMessage("Aloft:OnIsTargetDataChanged", "OnIsTargetDataChanged")
		self:RegisterMessage("Aloft:OnHealthBarValueChanged", "Update")
		self:RegisterMessage("Aloft:OnNameplateShow", "OnNameplateShow")
		self:RegisterMessage("Aloft:OnNameplateHide", "OnNameplateHide")
		self:RegisterMessage("Aloft:OnSetHealthBarColor", "OnSetHealthBarColor")

		self:RegisterMessage("SharedMedia_SetGlobal", function(message, mediatype, override)
			if mediatype == "statusbar" then
				AloftHealthBarDeficit:UpdateAll()
			end
		end)

		self:RegisterMessage("Aloft:SetAll", function(message, type, value)
			if AloftHealthBarDeficit.db.profile[type] then
				AloftHealthBarDeficit.db.profile[type] = value
				AloftHealthBarDeficit:UpdateAll()
			end
		end)
	end
end

function AloftHealthBarDeficit:UpdateAll()
	for aloftData in Aloft:IterateVisibleNameplates() do
		self:OnNameplateShow("AloftHealthBarDeficit:UpdateAll", aloftData)
		self:Update("AloftHealthBarDeficit:UpdateAll", aloftData)
	end
end

function AloftHealthBarDeficit:UpdateSizesAll()
	-- Aloft:GetModule("HealthBar"):UpdateAll()
	Aloft:GetModule("Frame"):UpdateAll()
	Aloft:GetModule("Overlay"):UpdateAll()
	Aloft:GetModule("HealthBar"):UpdateAll() -- TODO: take this out when modules are merged
	self:UpdateAll()
end

-----------------------------------------------------------------------------

function AloftHealthBarDeficit:OnIsTargetDataChanged(message, aloftData)
	-- ChatFrame7:AddMessage("AloftHealthBarDeficit:OnIsTargetDataChanged(): enter " .. tostring(aloftData.name) .. "/" .. tostring(aloftData.isTarget))
	self:ScheduleTimer(function(aloftData) AloftHealthBarDeficit:DoNameplateShow(aloftData) end, 0.1, aloftData) -- next frame
end

function AloftHealthBarDeficit:DoNameplateShow(aloftData)
	self:OnNameplateShow("AloftHealthBarDeficit:DoNameplateShow", aloftData)
end

function AloftHealthBarDeficit:OnNameplateShow(message, aloftData)
	aloftData.healthBar:Hide()
	self:AcquireDeficitFrame(aloftData)
end

function AloftHealthBarDeficit:OnNameplateHide(message, aloftData)
	-- ChatFrame7:AddMessage("AloftHealthBarDeficit:OnNameplateHide(): enter " .. tostring(aloftData.name) .. "/" .. tostring(aloftData.isTarget))
	self:ReleaseDeficitFrame(aloftData)
end

function AloftHealthBarDeficit:OnSetHealthBarColor(message, aloftData)
	self:SetNameplateDeficitColor(aloftData)
end

function AloftHealthBarDeficit:CalculateFrameWidth(aloftData, deficitFrame)
	local width
	if aloftData.healthBarValue and aloftData.healthBarMaxValue then
		local inset, _ = self:GetBorder(aloftData)

		local value = (self.db.profile.enable and (aloftData.healthBarMaxValue - aloftData.healthBarValue)) or aloftData.healthBarValue
		width = (value / aloftData.healthBarMaxValue) * (deficitFrame:GetWidth() - (2 * inset))
	end

	return width
end

function AloftHealthBarDeficit:Update(message, aloftData)
	local layoutFrame = aloftData.layoutFrame
	local deficitFrame = (layoutFrame and layoutFrame.deficitFrame) or self:AcquireDeficitFrame(aloftData)
	local healthRegion = deficitFrame and deficitFrame.healthRegion

	if deficitFrame and healthRegion then
		local width = self:CalculateFrameWidth(aloftData, deficitFrame)
		if width then
			if width > 0 then
				healthRegion:SetWidth(width)
				healthRegion:Show()
			else
				healthRegion:Hide()
			end
		end
		deficitFrame:Show()
	end
end

-----------------------------------------------------------------------------

function AloftHealthBarDeficit:AcquireDeficitFrame(aloftData)
	-- ChatFrame7:AddMessage("AloftHealthBarDeficit:AcquireDeficitFrame(): acquire " .. aloftData.name)
	local layoutFrame = aloftData.layoutFrame
	if not layoutFrame then
		layoutFrame = Aloft:AcquireLayoutFrame(aloftData)
	end
	if not layoutFrame then
		ChatFrame7:AddMessage("AloftHealthBarDeficit:AcquireDeficitFrame(): no layoutFrame " .. tostring(aloftData.name))
		ChatFrame7:AddMessage("AloftHealthBarDeficit:AcquireDeficitFrame(): " .. debugstack())
	end

	local deficitFrame = layoutFrame.deficitFrame
	if not deficitFrame then
		deficitFrame = CreateFrame("Frame", nil, layoutFrame)

		layoutFrame.deficitFrame = deficitFrame
		deficitFrame.layoutFrame = layoutFrame
	end

	self:SetupFrame("AloftHealthBarDeficit:AcquireDeficitFrame", aloftData)

	return deficitFrame
end

function AloftHealthBarDeficit:GetBorder(aloftData)
	if not self.healthBarDB.profile.targetOnly or (aloftData and (aloftData.isTarget or aloftData:IsTarget())) then
		return ((self.healthBarDB.profile.border ~= "None") and 4) or 0, SML:Fetch("border", self.healthBarDB.profile.border)
		-- return 0, SML:Fetch("border", "None")
	else
		return 0, nil
	end
end

function AloftHealthBarDeficit:SetupFrame(message, aloftData)
	local layoutFrame = aloftData.layoutFrame
	if not layoutFrame then
		layoutFrame = Aloft:AcquireLayoutFrame(aloftData)
	end
	if not layoutFrame then
		ChatFrame7:AddMessage("AloftHealthBarDeficit:SetupFrame(): no layoutFrame " .. tostring(aloftData.name))
		ChatFrame7:AddMessage("AloftHealthBarDeficit:SetupFrame(): " .. debugstack())
	end

	local deficitFrame = layoutFrame.deficitFrame
	if deficitFrame then
		local texture = SML:Fetch("statusbar", self.healthBarDB.profile.texture)
		local inset, edgeFile = self:GetBorder(aloftData)

		deficitFrame:ClearAllPoints()
		deficitFrame:SetPoint("TOPLEFT", layoutFrame, "TOPLEFT", self.healthBarDB.profile.offsets.left - inset, self.healthBarDB.profile.offsets.vertical + inset)
		deficitFrame:SetPoint("BOTTOMRIGHT", layoutFrame, "TOPRIGHT", self.healthBarDB.profile.offsets.right + inset, self.healthBarDB.profile.offsets.vertical - self.healthBarDB.profile.height - inset)
		deficitFrame:SetFrameLevel(aloftData.nameplateFrame:GetFrameLevel())
		deficitFrame:Show()

		local healthRegion = deficitFrame.healthRegion
		if not healthRegion then
			healthRegion = deficitFrame:CreateTexture(nil, "ARTWORK")
			healthRegion:SetBlendMode("BLEND")
			deficitFrame.healthRegion = healthRegion
		end
		healthRegion:SetTexture(texture)

		-- glue the right of the health region to the right of the deficit frame, we want motion on the left edge of this region; adjusting for the inset is handled by adjusting the width
		healthRegion:ClearAllPoints()
		if self.db.profile.enable then
			-- deficit mode
			healthRegion:SetPoint("TOPRIGHT", deficitFrame, "TOPRIGHT", -inset, -inset)
			healthRegion:SetPoint("BOTTOMRIGHT", deficitFrame, "BOTTOMRIGHT", -inset, inset)

			healthRegion:SetWidth(0)
			healthRegion:Hide()
		else
			-- normal mode
			healthRegion:SetPoint("TOPLEFT", deficitFrame, "TOPLEFT", inset, -inset)
			healthRegion:SetPoint("BOTTOMLEFT", deficitFrame, "BOTTOMLEFT", inset, inset)

			local width = self:CalculateFrameWidth(aloftData, deficitFrame)
			if width then healthRegion:SetWidth(width) end
			healthRegion:Show()
		end
		-- ChatFrame7:AddMessage("AloftHealthBarDeficit:SetupFrame(): health " .. healthRegion:GetDrawLayer())

		backdropTable.insets.left = inset
		backdropTable.insets.right = inset
		backdropTable.insets.top = inset
		backdropTable.insets.bottom = inset
		backdropTable.edgeFile = edgeFile
		backdropTable.bgFile = texture

		-- ChatFrame7:AddMessage("AloftHealthBarDeficit:SetupFrame(): " .. tostring(inset) .. "/" .. tostring(texture) .. "/" .. tostring(edgeFile))

		-- NOTE: this dead code is relevant to various #132 crash experiments 
		-- deficitFrame:SetBackdrop( { insets = { left = inset, right = inset, top = inset, bottom = inset, }, edgeFile = edgeFile, edgeSize = 16, bgFile = texture, } )
		-- if not self.healthBarDB.profile.targetOnly or (aloftData and (aloftData.isTarget or aloftData:IsTarget())) then
			-- deficitFrame:SetBackdrop( { insets = { left = 4, right = 4, top = 4, bottom = 4, },
			--							edgeSize = 16, edgeFile = [[Interface\Addons\Aloft\Textures\krsnik]],
			--							bgFile = [[Interface\AddOns\Forte_Core\Textures\Minimalist]], } )
			-- deficitFrame:SetBackdrop( { insets = { left = 4, right = 4, top = 4, bottom = 4, },
			--							edgeSize = 16, edgeFile = [[Interface\Addons\Aloft\Textures\krsnik]],
			--							bgFile = [[Interface\AddOns\Forte_Core\Textures\Minimalist]], } )
		-- else
			-- deficitFrame:SetBackdrop( { insets = { left = 0, right = 0, top = 0, bottom = 0, },
			--							edgeSize = 16, edgeFile = nil,
			--							bgFile = [[Interface\AddOns\Forte_Core\Textures\Minimalist]], } )
		-- end

		deficitFrame:SetBackdrop(backdropTable)
		deficitFrame:SetBackdropColor(unpack(self.healthBarDB.profile.colors.backdropColor))
		deficitFrame:SetBackdropBorderColor(unpack(self.healthBarDB.profile.borderColor))
		-- deficitFrame:SetAlpha(aloftData.nameplateFrame:GetAlpha())

		-- This manipulates the healthbar background to always display above the frame background
		-- ChatFrame7:AddMessage("AloftHealthBarDeficit:SetupFrame(): deficit #regions " .. deficitFrame:GetNumRegions())
		local _, backgroundRegion = deficitFrame:GetRegions()

		backgroundRegion:SetDrawLayer("BACKGROUND")
		backgroundRegion:SetBlendMode("BLEND")
		backgroundRegion:Show()

		-- ChatFrame7:AddMessage("AloftHealthBarDeficit:SetupFrame(): backdrop " .. tostring(aloftData.name) .. "/" .. tostring(layoutFrame:GetWidth()) .. "/" .. tostring(layoutFrame:GetHeight()))
		-- ChatFrame7:AddMessage("AloftHealthBarDeficit:SetupFrame(): health " .. tostring(aloftData.name) .. "/" .. tostring(inset) .. "/" .. tostring(deficitFrame:GetWidth()) .. "/" .. tostring(deficitFrame:GetHeight()))
	end
end

function AloftHealthBarDeficit:ReleaseDeficitFrame(aloftData)
	-- ChatFrame7:AddMessage("AloftHealthBarDeficit:ReleaseDeficitFrame(): release " .. aloftData.name)
	self:CleanupDeficitFrame(aloftData)
	self:RePoolDeficitFrame(aloftData)
end

function AloftHealthBarDeficit:CleanupDeficitFrame(aloftData)
	local layoutFrame = aloftData.layoutFrame
	local deficitFrame = layoutFrame and layoutFrame.deficitFrame
	if deficitFrame then
		self:ClearBackdrop(deficitFrame)
		-- if deficitFrame.healthRegion then deficitFrame.healthRegion:Hide() end
	end
end

function AloftHealthBarDeficit:RePoolDeficitFrame(aloftData)
	local layoutFrame = aloftData.layoutFrame
	local deficitFrame = layoutFrame and layoutFrame.deficitFrame
	if deficitFrame then
		deficitFrame:Hide()
	end
end

function AloftHealthBarDeficit:ClearBackdrop(deficitFrame)
	if deficitFrame then
		deficitFrame:SetBackdropColor(0, 0, 0, 0)
		deficitFrame:SetBackdropBorderColor(0, 0, 0, 0)

		-- NOTE: doing this on nameplate hide may case #132 crashes
		-- deficitFrame:SetBackdrop(defaultBackdropTable)
	end
end

function AloftHealthBarDeficit:SetNameplateDeficitColor(aloftData)
	-- ChatFrame7:AddMessage("AloftHealthBarDeficit:SetNameplateDeficitColor(): " .. aloftData.name .. " rgb = " .. aloftData.healthBarR .. "/" .. aloftData.healthBarG .. "/" .. aloftData.healthBarB)
	self:SetAtomicDeficitFrameColors(aloftData, aloftData.healthBarR, aloftData.healthBarG, aloftData.healthBarB, self.healthBarDB.profile.alpha)
end

function AloftHealthBarDeficit:SetDeficitFrameColor(aloftData, color)
	self:SetAtomicDeficitFrameColors(aloftData, color[1], color[2], color[3], self.healthBarDB.profile.alpha)
end

function AloftHealthBarDeficit:SetAtomicDeficitFrameColors(aloftData, hbr, hbg, hbb, hba)
	local layoutFrame = aloftData.layoutFrame
	local deficitFrame = layoutFrame and layoutFrame.deficitFrame
	if deficitFrame and deficitFrame.healthRegion then
		-- local r, g, b, a = self:GetSemiQuasiPseudoComplementColor(hbr, hbg, hbb, hba)
		local r, g, b, a = hbr, hbg, hbb, hba
		deficitFrame.healthRegion:SetVertexColor(r, g, b, a)

		-- read these back in; apparently the concern is that there is rounding error
		aloftData.healthBarR, aloftData.healthBarG, aloftData.healthBarB, aloftData.healthBarA = deficitFrame.healthRegion:GetVertexColor()
	end
end

function AloftHealthBarDeficit:GetSemiQuasiPseudoComplementColor(r, g, b, a)
	local cr = 1.0 - r
	local cg = 1.0 - g
	local cb = 1.0 - b
	local ca = a

	return cr, cg, cb
end

-----------------------------------------------------------------------------

-- end)
